//
// Created by tang on 2022/1/10.
//

#include "app.hpp"
#include "gui/diary_main_form.hpp"
#include "utils/path_utils.hpp"
#include "sql/sqlite3_driver.hpp"
#include "sql_driver_loader.hpp"
#include <fmt/format.h>
#include "app/version.h"
#include "extension/extension_manager.hpp"
#include "utils/single_app.hpp"
#include "utils/misc_utils.hpp"
#include "utils/log.hpp"

#if defined(_WIN32)

// MiniDump工具

#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.Lib")

static void CreateDumpFile(LPCWSTR strPath, EXCEPTION_POINTERS* pException)
{
    // 创建Dump文件;
    HANDLE hDumpFile = CreateFileW(strPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    // Dump信息;
    MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
    dumpInfo.ExceptionPointers = pException;
    dumpInfo.ThreadId = GetCurrentThreadId();
    dumpInfo.ClientPointers = FALSE;

    // 写入Dump文件内容;
    MiniDumpWriteDump (GetCurrentProcess (), GetCurrentProcessId (), hDumpFile,
                       (MINIDUMP_TYPE) (MiniDumpFilterMemory | MiniDumpScanMemory), &dumpInfo, NULL, NULL);
    CloseHandle(hDumpFile);
}

static LONG ApplicationCrashHandler(EXCEPTION_POINTERS* pException)
{
    WCHAR szDumpDir[MAX_PATH] = { 0 };
    WCHAR szDumpFile[MAX_PATH] = { 0 };
    WCHAR szMsg[MAX_PATH] = { 0 };
    SYSTEMTIME	stTime = { 0 };

    // 构建dump文件路径;
    GetLocalTime(&stTime);
    ::GetCurrentDirectoryW(MAX_PATH, szDumpDir);
    _swprintf(szDumpFile, L"%s\\%04d%02d%02d_%02d%02d%02d.dmp", szDumpDir,
        stTime.wYear, stTime.wMonth, stTime.wDay,
        stTime.wHour, stTime.wMinute, stTime.wSecond);

    // 创建dump文件;
    CreateDumpFile(szDumpFile, pException);

    // 弹出一个错误对话框或者提示上传， 并退出程序;
    _swprintf(szMsg, L"I'm so sorry, but the program crashed.\r\ndump file : %s", szDumpFile);
    FatalAppExitW(-1, szMsg);

    return EXCEPTION_EXECUTE_HANDLER;
}

static void ConfigMiniDump()
{
    SetUnhandledExceptionFilter(ApplicationCrashHandler);
}
#endif

int main(int argc, char* argv[])
{
#if defined(_WIN32)
    ConfigMiniDump();
    std::setlocale(LC_ALL, "zh_CN.UTF-8");
#endif

    int narg = std::find_if(argv, argv + argc, [](const char* arg){ return 0 == strcmp("--wait", arg); }) - argv;
    if (narg < argc)
    {
        SingleAppGuard::Instance ().Wait();
    }

    L_INFO("%s", get_version_magic_string());
    L_INFO("DiaryApp %s(%llx) start...", get_version_string(), get_version_code());
    L_INFO("Log File: %s", log_file_path());

    if (SingleAppGuard::Instance ().CheckRunning ())
    {
        auto const pid = SingleAppGuard::Instance().InstancePID();
        L_INFO("DiaryApp already running, pid is %ld", pid);
        ActiveProcessWindow(SingleAppGuard::Instance().InstancePID());
        return 0;
    }

    const auto driver_dir = "sql"_AP;
    Application::RegisterSqlDriver(GetSqlite3Driver());
    AddDriverFolder(driver_dir.c_str());

    Application app;
    app.SetupSqlDriver();

    // load scripts
    ExtensionManager::singleton().LoadExtensions();

    DiaryAppMainForm mainForm{fmt::format("Diary Tool C++ v{}({:x})", get_version_string(), get_version_code()), 1280, 720};
    app.Run(&mainForm);

    // free scripts
    ExtensionManager::singleton().FreeExtensions();

    L_INFO("DiaryApp %s(%llx) exit...", get_version_string(), get_version_code());

    return 0;
}
